package com.zms.maze;

import java.util.ArrayList;
import java.util.List;

import com.zms.maze.MummyConst.Direction;

import Lib.GenericTree;
import Lib.GenericTreeNode;

public class PathFinder {
	private Transaction builder;
	private List<Maze> stateWin;
	private GenericTree<Action> tree;
	private List<GenericTree<Action>> listTree=new ArrayList<GenericTree<Action>>();
	public PathFinder(Transaction transaction,List<Maze> stateWIn)
	{
		this.builder=transaction;
		this.stateWin=stateWIn;
		buildMiniTree();
	}
	//Building tree algorithms:
	//Input:+ list of final mazes.
	//		+ transaction object.
	//Output: the tree presents all transaction in maze.
	//Algorithms:
	//Declare a leafList contains leaf nodes which have highest level. Init leafList={root}.
	//While leafList.count>0
	//	+ foreach node in leafList:
	//			+ get all childrens of this node.
	//			+ foreach childNode:
	//				if (node isn't exist in tree):
	//					+  add childNode into node.
	//					+  add childNode into new leafList
	//	+ refresh the leafList: leafList=newLeafList
	//
	
	//This tree is archieved. it's smaller than the above tree
	public void buildMiniTree()
	{
		List<GenericTreeNode<Action>> leafList=new ArrayList<GenericTreeNode<Action>>();
		List<GenericTreeNode<Action>> newLeafList=new ArrayList<GenericTreeNode<Action>>();
		GenericTreeNode<Action> childNode;
		Action action;
		for(Maze finalMaze:stateWin){
			action=new Action(finalMaze, Direction.WAIT);
			GenericTreeNode<Action> node=new GenericTreeNode<Action>(action);
			tree=new GenericTree<>();
			tree.setRoot(node);
			leafList.add(node);
			while(leafList.size()>0)
			{
				Utilities.getInstance().log(tree.getNumberOfNodes());
				for(GenericTreeNode<Action> leafNode:leafList)
				{
					List<Action> listActions=this.builder.getPreviousMaze(leafNode.getData().getMaze());
					for(Action act:listActions)
					{
						if (tree.breadthFind(act)==null)
						{
							//add child node into leaftNode
							childNode=new GenericTreeNode<Action>(act);
							leafNode.addChild(childNode);
							newLeafList.add(childNode);
						}
					}
				}
				leafList.clear();
				leafList=new ArrayList<GenericTreeNode<Action>>(newLeafList);
				newLeafList.clear();
			}
			listTree.add(tree);
		}
	}
	public void buildTree()
	{
		List<GenericTreeNode<Action>> leafList=new ArrayList<GenericTreeNode<Action>>();
		List<GenericTreeNode<Action>> newLeafList=new ArrayList<GenericTreeNode<Action>>();
		GenericTreeNode<Action> childNode;
		Action action;
		for(Maze finalMaze:stateWin){
			action=new Action(finalMaze, Direction.WAIT);
			GenericTreeNode<Action> node=new GenericTreeNode<Action>(action);
			tree=new GenericTree<>();
			tree.setRoot(node);
			leafList.add(node);
			while(leafList.size()>0)
			{
				Utilities.getInstance().log(tree.getNumberOfNodes());
				for(GenericTreeNode<Action> leafNode:leafList)
				{
					List<Action> listActions=this.builder.getPreviousMaze(leafNode.getData().getMaze());
					for(Action act:listActions)
					{
						if (tree.find(act)==null)
						{
							//add child node into leaftNode
							childNode=new GenericTreeNode<Action>(act);
							leafNode.addChild(childNode);
							newLeafList.add(childNode);
						}
					}
				}
				leafList.clear();
				leafList=new ArrayList<GenericTreeNode<Action>>(newLeafList);
				newLeafList.clear();
			}
			listTree.add(tree);
		}
	}

	public List<GenericTree<Action>> getTreeCollection()
	{
		return this.listTree;
	}
	public void discoveryTree()
	{
		discovery(tree.getRoot());
	}
	
	private void discovery(GenericTreeNode<Action> nnode)
	{
		Action action;
		int level;
		for(GenericTreeNode<Action> node:nnode.getChildren())
		{
			action=node.getData();
			level=tree.getLevelOfNode(node);
			String log=String.valueOf(level)+": "+action.getMaze().explorer.getPosString()+"-"+action.getMaze().mummies.get(0).getPosString()+action.getDriString();
			Utilities.getInstance().log(log);
			discovery(node);
		}
	}
	
	public List<String> getDirectionsToRoot(GenericTree<Action> ntree,Maze maze)
	{
		List<String> paths=new ArrayList<String>();
		GenericTreeNode<Action> node=ntree.breadthFind(new Action(maze,Direction.WAIT));
		do
		{
			paths.add(node.getData().getDriString());
			node=node.getParent();
		}while(node!=null);
		return paths;
		
	}
	
}
